﻿using System;
using System.Collections.Generic;

namespace LibMQTT.TCPServices
{
    using System.Net.Sockets;
    using ProtocalIMPL;
    using System.Linq;

    /// <summary>
    /// 一些常用的扩展 替换TCP/UDP标准的同步发送函数
    /// </summary>
    public static class HyperRExtension
    {
        /// MQTT服务层内核函数 - 将任意比特流转换为 IBM公司Mqtt报文
        public static void MqttQoSSend(this Socket refChild,
            IBMComPacketType CalleeComType, IEnumerable<byte> data)
        {
            // 1.先发送 必定存在的1~5字节`固定报文` 
            var Compacket = new MqCompact(CalleeComType, data);
            int PACKSIZE = MqLotBase._BlockSize;
            byte[] H1Data = Compacket.H1Stream;
            byte[] H23Data = Compacket.H23Stream;
            int N1 = refChild.Send(H1Data);
            if (N1 > 0)
            {
                // 2.再发送 可能存在的H2区 `可变报文` 注意这个部分的发送 可能是很大的 但最大不超过 256Mb
                switch (CalleeComType)
                {
                    case IBMComPacketType.PingReq:
                    case IBMComPacketType.PingResp:
                    case IBMComPacketType.DisConnect:
                        break;
                    case IBMComPacketType.Connect:
                    case IBMComPacketType.SubScribe:
                    case IBMComPacketType.Publish:
                        _SendTCPInternal(refChild, H23Data,PACKSIZE);
                        break;
                }
            }
            else throw new NotImplementedException("MQTT3.1报文异常 - [H1固定报文]传输失败~");
        }

        /// <summary>
        /// MQTT报文发送函数 : `通用TCP免丢包传输函数()`
        /// </summary>
        internal static void _SendTCPInternal(Socket refChild, IEnumerable<byte> data , int _packSize)
        {
            //具体的 TCP/UDP 同步函数 同`Socket标准函数Send()` 一次发送多片儿
            int page, rest;
            rest = data.Count() % _packSize;
            page = data.Count() / _packSize + ((rest == 0) ? 0 : 1);
            int N51 = 0, times = 0;
            while (N51 < data.Count())
            {
                int pos = times * _packSize;
                int end = (times == page - 1) ? rest : _packSize;
                int N15 = refChild.Send(data.ToArray<byte>(), pos, end, SocketFlags.None);
                if (N15 > 0)
                {
                    times++;
                    N51 += N15;
                }
                Console.WriteLine("发送 TCP分片儿大小[{1}:{0}kb]", N15, times);
            }
        }

        /// <summary>
        /// 在MqLot服务程序内部编写的 `传输解码函数`
        /// 根据数据H1stDo 解析数据2区的动态内容 最长256Mb
        /// 本次解析不包含数据1区 B1在上一次被解析 长度固定5字节
        /// </summary>
        /// <param name="refChild">Sock文件描述符</param>
        /// <param name="CompaType">MQTT报文类实例</param>
        /// <returns>转换之后的`任意`UTF8比特流</returns>
        public static List<byte> MqttQoSRecv(this Socket refChild , 
            out IBMComPacketType CompaType)
        {
            /// 根据数据H1stDo 解析数据2区的动态内容 最长256Mb
            var result = new List<byte>();
            /* 1.解码固定报文 */
            byte[] b1 = new byte[5];
            int N = refChild.Receive(b1);
            // Out参数 InOut内部返回给上层调用者 告诉其本次报文是什么类型 PayLoad长度是多少
            CompaType = MqLotLambda._DeFixH1(b1[0]);
            byte[] FLenBuff = new byte[4];
            int end = (b1.Length - 1);
            Buffer.BlockCopy(b1, 1, FLenBuff, 0, end);
            var PayLen = MqLotLambda._DeVLen(FLenBuff);
            result.AddRange(b1); // 123区都要整体拼装后返回

            /* 2.获取2区Dynamic动态数据区长度 */
            int times = 0;
            while (PayLen > 0)
            {
                byte[] once = new byte[MqLotBase._BlockSize];
                int NX = refChild.Receive(once, 0, once.Length, SocketFlags.None);
                if (NX > 0)
                {
                    for (int i = 0; i < NX; i++)
                        result.Add(once[i]);
                    PayLen -= NX;
                    times++;
                }
                Console.WriteLine("Ping针实际传输次数 ---> " + times);
            }
            return result;
        }
    }
}
